package convert

import (
	"bufio"
	"context"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"gitee.com/general252/ffmpeg-tool/help"
)

type VideoCodec string

const (
	H264 VideoCodec = "h264"
	H265 VideoCodec = "hevc"
)

// Video 视频转换
// FFmpeg 视频参数：
// -b 设置视频流量，默认为 200Kbit/s  -b:v 1500K  -b:v 2M
// -r 设置帧速率，默认为 25
// -s 设置画面的宽与高
// -aspect 设置画面的比例(–aspect 16:9)
// -vn 不处理视频
// -vcodec 设置视频编解码器，未设定时则使用与输入流相同的编解码器，一般后面加 copy 表示拷贝。
// -bf 0 表示无B帧
// -keyint_min 最小关键帧间隔
// -sc_threshold 会根据视频的运动场景，自动为你添加额外的I帧 0
//
// ffmpeg -ss 00:18:40 -t 00:04:00 -i "小破球2.HD1080p.国语中字.mp4" -c:v libx264 -s 640x480 -bf 0 -an -y -keyint_min 25 -g 25  -sc_threshold 0 -f h264 output4.h264
// 去除SEI
// ffmpeg -i input.h264 -codec copy -bsf:v "filter_units=remove_types=6" output_nosei.h264
type Video struct {
}

func NewVideo() *Video {
	return &Video{}
}

// Convert 转码
// size: 640x480
// bitRate: 1500K 2M
// keyInt: 关键帧间隔 25
func (tis *Video) Convert(ctx context.Context, inFile, outFile string,
	start time.Duration, duration time.Duration,
	codec VideoCodec, fps int, keyInt int, size string, bitRate string,
	noSEI bool,
	onmessage func(msg string)) (string, error) {

	var codecId = string(codec)
	switch codec {
	case H264:
		codecId = "libx264"
		if len(filepath.Ext(outFile)) == 0 {
			outFile += ".h264"
		}
	case H265:
		codecId = "libx265"
		if len(filepath.Ext(outFile)) == 0 {
			outFile += ".h265"
		}
	}

	var args []string
	if start > 0 {
		args = append(args, "-ss", help.GetDurationString(start))
	}
	if duration > 0 {
		args = append(args, "-t", help.GetDurationString(duration))
	}
	args = append(args, "-i", inFile)
	if len(bitRate) > 0 {
		args = append(args, "-b:v", bitRate)
	}
	if fps > 0 {
		args = append(args, "-r", strconv.Itoa(fps))
	}
	if keyInt > 0 {
		args = append(args, "-keyint_min", strconv.Itoa(keyInt))
		args = append(args, "-g", strconv.Itoa(keyInt))
		args = append(args, "-sc_threshold", "0")
	}
	if len(size) > 0 {
		args = append(args, "-s", size)
	}

	args = append(args, "-bf", "0")
	args = append(args, "-c:v", codecId)
	args = append(args, "-an")
	args = append(args, "-y")
	args = append(args, "-f", string(codec))
	args = append(args, outFile)

	onmessage(colorMessage(strings.Join(append([]string{"ffmpeg"}, args...), " ")))
	cmd := exec.CommandContext(ctx, "ffmpeg", args...)
	r, err := cmd.StderrPipe()
	if err != nil {
		return "", err
	}

	go tis.readConsole(r, onmessage)

	if err := cmd.Run(); err != nil {
		return "", err
	}

	if noSEI {
		onmessage(colorMessage("去除SEI"))
		// ffmpeg -i input.h264 -codec copy -bsf:v "filter_units=remove_types=6" output_nosei.h264
		var tmpOutFile string
		var tmpArgs []string

		switch codec {
		case H264:
			tmpOutFile = outFile + ".h264"
			tmpArgs = []string{
				"-i", outFile,
				"-c:v", "copy",
				"-bsf:v", "filter_units=remove_types=6",
				tmpOutFile,
			}
		case H265:
			tmpOutFile = outFile + ".h265"
			tmpArgs = []string{
				"-i", outFile,
				"-c:v", "copy",
				"-bsf:v", "filter_units=remove_types=35|38-40",
				tmpOutFile,
			}
		}

		tmpCmd := exec.CommandContext(ctx, "ffmpeg", tmpArgs...)
		r, err := tmpCmd.StderrPipe()
		if err != nil {
			return "", err
		}
		go tis.readConsole(r, onmessage)
		if err := tmpCmd.Run(); err != nil {
			return "", err
		}

		onmessage(colorMessage("文件重命名"))
		if err = os.Remove(outFile); err != nil {
			return "", err
		}
		if err = os.Rename(tmpOutFile, outFile); err != nil {
			return "", err
		}
	}

	return outFile, nil
}

func (tis *Video) readConsole(r io.ReadCloser, onmessage func(msg string)) {
	defer r.Close()

	rr := bufio.NewReader(r)
	for {
		ch, err := rr.ReadString('\r')
		if err != nil {
			return
		}

		msg := ch

		msg = strings.TrimLeft(msg, "\r")
		msg = strings.TrimLeft(msg, "\n")

		msg = strings.TrimRight(msg, "\r")
		msg = strings.TrimRight(msg, "\n")
		onmessage(msg)
	}
}

func colorMessage(msg string) string {
	return fmt.Sprintf("<font color='green'><h3>%v</h3></font>", msg)
}
